1. Points divers…​

  • Nuit de l’info

  • PTut et Cahier d’Analyse et de Conception

  • Stages

2. Suivre ces slides …​

http://bit.ly/jmb-cpoa
http://bit.ly/jmb-cpoa
http://iut-blagnac.github.io/cpoa/

3. Introduction

Ce cours porte sur la Conception et Programmation Objet Avancée.

4. Concepts objets

Vous avez appris (cf. M2103 et M2104) un certain nombre de concepts objets :

  • Abstraction

  • Encapsulation

  • Héritage

  • Polymorphisme

5. Abstraction

Définition :

6. Abstraction

Définition (restrictive) :

Une classe est une abstraction des caractéristiques communes d’un ensemble d’objets.

7. Encapsulation

Définition :

8. Encapsulation

Définition (restrictive) :

Dans la description d’un objet, le but de l'encapsulation est de masquer les attributs et les méthodes, c’est-à-dire, la manière dont est réalisé le comportement de l’objet.

9. Héritage

Définition :

10. Héritage

Définition (simpliste) :

L’héritage est la transmission de caractéristiques à ses descendants.

La classe qui hérite dispose des méthodes et attributs de niveau public et protected de sa classe mère.

11. Polymorphisme

Le nom de polymorphisme vient du grec :

12. Polymorphisme

Le nom de polymorphisme vient du grec :

qui peut prendre plusieurs formes

L’héritage concerne les classes, le polymorphisme concerne les objets.

13. Polymorphisme (suite)

On distingue généralement trois types de polymorphisme :

  • Le polymorphisme ad hoc (également surcharge ou en anglais overloading)

  • Le polymorphisme paramétrique (également généricité ou en anglais template)

  • Le polymorphisme d’héritage (également redéfinition, spécialisation ou en anglais overriding)

14. Polymorphisme ad hoc

  • Appelé aussi surcharge.

  • Permet d’avoir des fonctions de même nom dans des classes sans aucun rapport entre elles.

  • Permet de définir des opérateurs d’utilisation différente en fonction des paramètres.

int method(int,int);
int method(int);
int method(float,float);

15. Le polymorphisme paramétrique

Appelé aussi généricité.

interface Iterator<E> {
    boolean hasNext();
    E next();
}

public <T> static void copy(Collection<? extends T> source, Collection<? super T> dest) {
    for (T t : source) {
         dest.add(t);
    }
}

16. Le polymorphisme d’héritage

  • Appelé aussi spécialisation (ou redéfinition).

  • Lié à la redéfinition des méthodes héritées.

17. Objectifs de la conception objet

On essaye d’éviter trois problèmes principaux du développement :

La rigidité

Anticiper les évolutions susceptibles d’impacter l’application.

La fragilité

Eviter les erreurs provoquées par la modification d’une partie du code.

L’immobilité

Rendre moins difficile l’extraction d’une partie du code.

18. Bonnes pratiques et patrons

Pour répondre aux problèmes ci-dessus, on va s’attaquer à diminuer les dépendances et éviter l'"effet spaghetti".

Les qualités recherchées sont :

  • Robustesse : les changements n’introduisent pas de régressions.

  • Extensibilité : il est facile d’ajouter de nouvelles fonctionnalités.

  • Réutilisabilité : il est possible de réutiliser certaines parties de code pour construire d’autres applications.

19. Bonnes pratiques et patrons (suite)

Nous allons apprendre des bonnes pratiques :

  • Identifier les aspects qui varient et les séparer des aspects constants

  • Programmer une interface, non une implémentation

  • Préférer la composition à l’héritage

  • Les classes doivent être ouvertes à l’extension, mais fermées à la modification

  • Dépendez d’abstractions. Ne dépendez pas de classes concrètes (inversion des dépendances)

  • Ne parlez pas aux inconnus

20. Bonnes pratiques et patrons (suite)

L’étape suivante consiste à apprendre les bonnes solutions de conception, ce qu’on appelle les patrons de conception (ou design patterns en anglais).

21. Organisation du cours

Rappel du rythme : 1 cours, 1 TD et 2 TPs par semaine. Pendant 8 semaines.

  • La première semaine est consacrée aux principes généraux des patrons de conception, en partant d’un exemple (cours en fin de semaine).

  • Les 5 ou 6 suivantes sont consacrées à l’étude de certains patrons classiques. Mise en pratique sur des exercices en TP.

    Le cours est inversé par rapport aux habitudes : Conception et étude d’un ou plusieurs patrons semaine N; mise en oeuvre en TP semaine N+1; puis cours en amphi (détails, discussions) en semaine N+2.

  • Les 2 ou 3 dernières semaines, les étudiants sont en mode projet pour faire du refactoring d’applications réelles (conception aidée en TD sur les modèles UML™, mise en oeuvre en TP).

22. Organisation (suite)

Voici une proposition de déroulement des semaines :

Semaine 1

SuperCanard, le grand classique, Strategy

Semaine 2
Semaine 3

Patrons Le patron Fabrique, Proxy, Etat

Semaine 4
  • Observateur

    • version intuitive (2 interfaces)

    • version Java (classe Observable)

Semaine 5

L’exemple de Meyer : menus en objet

Semaine 6
  • Patrons Décorateur, Façade, Visiteur

  • MVC avec l’exemple JTable de Java

  • Patrons Chaîne de responsabilité (juste en cours)

Semaines 7 et 8
  • Quelques idées de projet final :

    • Refactorer un code généré par Umple.

    • Refactorer le code de MPA (mais pas le leur, celui d’un autre groupe)

23. Evaluation et notation

Comme prévu par le planning des contrôles, les étudiants auront :

  • une note de projet (TPs + projet final) ⇒ coef. 1

  • une note d’examen final (semaine 5) ⇒ coef. 2

24. Rappels sur des éléments Java importants

25. Importance du typage

26. Différents types de typage

Le fait d’attribuer un type (une classe) à une variable (un objet) peut se faire de plusieurs façons :

  • statique

  • dynamique

  • duck typing

27. Typage statique

On parle de typage statique quand la majorité des vérifications de type sont effectuées au moment de la compilation.

Exemple de typage statique
int i = 0;  // cette déclaration indique explicitement que
            // la variable i est de type entier

28. Typage dynamique

Le typage dynamique consiste à laisser l’ordinateur réaliser l’opération de typage à la volée, lors de l’exécution du code.

29. Typage dynamique (exemple)

Exemple de typage dynamique
/**
 * @author André Peninou
 */
public class Type {
  void m() {
    System.out.println ("Type");
    }
}
public class SousType extends Type {
  void m() {
    System.out.println ("SousType");
  }
  void autreM(){
    System.out.println ("Spécifique SousType");
  }
}
...
    Type a = new Type();
    a.m(); // "Type"

    a = new SousType();
    a.m(); // "SousType"
    // Statique : a est un Type (à la compil)
    // Dynamique : a est un SousType au runtime.

    // D'où :
    a = new SousType();
    a.autreM();
    // NOK car type statique == A => autreM() n'existe pas à la compilation
...

30. Duck typing

Style de typage dynamique où la sémantique d’un objet (c’est-à-dire son type) est déterminée par l’ensemble de ses méthodes et de ses attributs, et non par un type défini et nommé explicitement par le programmeur.

L’origine de cette expression est liée à cette citation :

Si je vois un animal qui vole comme un canard, cancane comme un canard, et nage comme un canard, alors j’appelle cet oiseau un canard.

— James Whitcomb Riley

31. Duck typing (exemple)

Exemple de duck typing en Ruby
def calcule(a, b, c)
  return a*b+c
end

$a = calcule(6, 3, 2)
$b = calcule('6', 3, ', the number of the beast')

puts $a.to_s
puts $b.to_s

Ce qui donne :

20
666, the number of the beast

32. Importance de la visibilité

Dès que l’on commence à avoir une application conséquente, l’organisation en package devient obligatoire. Revenons donc sur les questions de visibilité des propriétés et méthodes, qui seront importants dans la plupart des aspects de ce module.

Si un champ d’une classe A :

  • est private, il est accessible uniquement depuis sa propre classe ;

  • a la visibilité package (visibilité par défaut, pas de mot-clef), il est accessible de partout dans le paquetage de A mais de nulle part ailleurs ;

  • est protected, il est accessible de partout dans le paquetage de A et, si A est publique, grosso modo dans les classes héritant de A dans d’autres paquetages ;

  • est public, il est accessible de partout dans le paquetage de A et, si A est publique, de partout ailleurs.

Ci-dessus, les niveaux de visibilité sont rangés par visibilité croissante.

33. Importance de la visibilité (exemple)

package UN;
public class A {
  protected String attrprotected;
  String attrfriend; // friend
}

Si on définit une deuxième classe dans le même package :

package UN;
  class B {
    ...
    {
        A a = new A ();
        a.attrprotected// OK : même si bizarre
        a.attrfriend  // OK : visible package
    }
  }

package UN;
  class C extends A {
    ...
    {
        this.attrprotected// OK : normal
        this.attrfriend // OK : visible package
    }
  }

34. Importance de la visibilité (suite de l’exemple)

package DEUX;
  class B {
      ...
    {
        A a = new A ();
        a.attrprotected// NON OK : normal
        a.attrfriend  // NON OK : normal, proche de "private"
    }
  }

  class C extends A {
      ...
    {
        this.attrprotected// OK : normal car protected et héritage
        this.attrfriend // NON OK : normal, proche de "private"
    }
  }

35. Alors private ou protected ?

À la question private ou protected ? Quel est le mieux pour les attributs ?

  • C’est une question de style de programmation !

  • Puristes (cf. [Meyer]) ⇒ private

  • Parfois utile : cf. Strategy, évite les getters/setters

36. Visibilité et UML

Il n’y a pas de visibilité par défaut en UML™.

37. Retour sur les Membres static

class VariableDemo
{
  static int count=0;
  public void increment()
  {
    count++;
  }
  public static void main(String args[])
  {
    VariableDemo obj1=new VariableDemo();
    VariableDemo obj2=new VariableDemo();
    obj1.increment();
    obj2.increment();
    System.out.println("Obj1: count is="+obj1.count);
    System.out.println("Obj2: count is="+obj2.count);
  }
}

Output:

Obj1: count is=2
Obj2: count is=2

38. Membres static (suite)

Comment ça marche :

  • Les variables static sont initialisées au chargement de la classe.

  • Les variables static d’une classe sont initialisées avant que la moindre instance ne soit créée.

  • Les variables static sont initialisées avant que la moindre méthode static ne s’exécute.

39. Méthodes static

import java.lang.Math;

class Another {
  public static void main(String[] args) {
    int result;

    result = Math.min(10, 20); //calling static method min by writing class name

    System.out.println(result);
    System.out.println(Math.max(100, 200));
  }
}

40. Méthodes static et appel aux méthodes non-statiques

public class Main {
    public static void main(String[] args) {
        Main p = new Main();
        k();
    }

    protected Main() {
        System.out.print("1234");
    }

    protected void k() {
    }
}

À l’exécution :

Main p = new Main();  // => prints 1234
k()                   // => raises error

41. Méthodes static et appel aux méthodes non-statiques (suite)

Static method cannot call non-static methods

Bien sûr que si, sauf qu’il faut que cette dernière porte sur une instance de la classe.

42. Méthodes static et appel aux méthodes non-statiques (suite)

Constructors are kind of a method with no return type.

En fait il vaudrait mieux les considérer comme une sorte de méthode statique. En effet elle ne requièrent pas de porter sur un objet!

43. Utilité générale des enum

44. Modélisation

Le type enumération est souvent utilisé en modélisation :

enum

45. Propriétés

public enum Civilite {
  MADAME, MONSIEUR
}
  • Chaque élément d’une énumération est un objet à part entière

  • Les objets enum héritent de java.lang.Enum

  • On peut compléter les comportements des objets en ajoutant des méthodes

46. Méthodes de base

  • toString()

    System.out.println(Civilite.MADAME); //MADAME
  • valueOf()

    Civilite civilite = Civilite.valueOf("MONSIEUR") ;
  • values()

    Civilite[] civilites = Civilite.values() ;
  • ordinal()

    Civilite civilite = Civilite.MONSIEUR ;
    System.out.println("Civilite : " + civilite + " [" + civilite.ordinal() + "]") ;
    // Civilite : MONSIEUR [1]
    Le 1er numéro d’ordre est 0.
  • compareTo()

    System.out.println(Civilite.MADAME.compareTo(Civilite.MONSIEUR)) ;
    // -1

47. Exemple plus complexe

public enum Langage {
  //Objets directement construits
  JAVA("Langage JAVA", "Eclipse"),
  C ("Lanage C", "Code Block"),
  CPlus ("Langage C++", "Visual studio"),
  PHP ("Langage PHP", "PS Pad");

  private String name = "";
  private String editor = "";

  //Constructeur
  Langage(String name, String editor){
    this.name = name;
    this.editor = editor;
  }

  public void getEditor(){
    System.out.println("Editeur : " + editor);
  }

  public String toString(){
    return name;
  }

  public static void main(String args[]){
    Langage l1 = Langage.JAVA;
    Langage l2 = Langage.PHP;

    l1.getEditor();
    l2.getEditor();
  }
}

48. Questions?

Important

49. Time for a quizz!

Warning
QUESTION
  • Connectez-vous sur : http://www.socrative.com/ (student login)

  • Ou téléchargez l’application pour étudiant socrative2

  • Choisissez la room 44918d67

socrative1

50. Construire ses applications

Pour générer un programme, une documentation, à partir des sources, on peut :

  • Soit utiliser un environnement intégré comme eclipse

  • Soit construire les sorties (on parle de Build) à partir des sources

Nous nous intéressons dans cette section à cette deuxième catégorie.

51. Construire ses applications (suite)

Il existe plusieurs outils :

52. Les scripts

Un script shell
#!/bin/sh
UML='model.uml'
TYPE='PNG'
MAINPATH='/Users/bruel/localdev/cpoa'
DOCLETPATH=$MAINPATH/doclet
PUMLPATH=$MAINPATH/util
echo "Creating $UML..."
echo $DOCLETPATH

javadoc \
-private \
-quiet \
-J-DdestinationFile=$UML \
-J-DcreatePackages=false \
-J-DshowPublicMethods=true \
-J-DshowPublicConstructors=false \
-J-DshowPublicFields=true \
-doclet de.mallox.doclet.PlantUMLDoclet -docletpath $DOCLETPATH/plantUmlDoclet.jar \
-sourcepath . src/Canard.java src/Colvert.java

echo "Done creating plantUML model"

TYPE='png'
echo "Converting $UML to $TYPE..."
java -jar $PUMLPATH/plantuml.jar \
  -config $PUMLPATH/config.cfg \
  -t $TYPE $UML
echo "Done generating PNG from model"

53. Les scripts (suite)

Un script batch Windows
set UML=TD1.uml
set TYPE='PNG'
set DOCLETPATH=E:\IUT-S3\CPOA\TP1\SuperCanardBof

echo "Creating %UML%..."
rem javadoc -private -quiet -J-DdestinationFile=%UML% -J-DcreatePackages=false -J-DshowPublicMethods=true -J-DshowPublicConstructors=false -J-DshowPublicFields=true -doclet de.mallox.doclet.PlantUMLDoclet -docletpath %DOCLETPATH%\plantUmlDoclet.jar src\canard\*.java

javadoc -J-DdestinationFile=%UML% -J-DcreatePackages=false -J-DshowPublicMethods=true -J-DshowPublicConstructors=false -J-DshowPublicFields=true -doclet de.mallox.doclet.PlantUMLDoclet -docletpath plantUmlDoclet.jar src\appli\*.java src\armes\*.java src\armes\impl\*.java
echo "Done."

set TYPE='png'
echo "Converting %UML% to %TYPE%..."
java -jar %DOCLETPATH%\plantuml.jar -config "%DOCLETPATH%\config.cfg" -t %TYPE% %UML%
echo "Done."

54. Les scripts (suite)

Avantages
  • Faciles

  • Rapides

  • Beaucoup d’exemples

Inconvénients
  • Pas portables sur d’autres systèmes (no comment ;-)

  • Peu lisibles

  • Peu évolutifs

55. make

Un Makefile pour générer ces cours
 1 #-----------------------------------------------------
 2 ICONSDIR=images/icons
 3 IMAGESDIR=images
 4 STYLE=/Users/bruel/Dropbox/Public/dev/asciidoc/stylesheets/golo-jmb.css
 5 DOCTOR=asciidoctor -a icons -a iconsdir=$(ICONSDIR) -a images=$(IMAGESDIR) -a source-highlighter=$(HIGHLIGHT)
 6 DECK=swiss
 7 EXT=asc
 8 PANDOC=pandoc
 9 OUTPUT=.
10 DEP=definitions.txt glossaire.txt refs.txt
11 #-----------------------------------------------------
12 
13 all: $(OUTPUT)/*.html
14 
15 images/%.png: images/%.plantuml
16 @echo '==> Compiling plantUML files to generate PNG'
17 java -jar plantuml.jar $<
18 
19 %.html: %.$(EXT) $(DEP)
20 @echo '==> Compiling asciidoc files with Asciidoctor to generate HTML'
21 $(DOCTOR) -a toc2 -b html5 -a numbered -a eleve $<
22 
23 %.deckjs.html: %.$(EXT)  $(DEP)
24 @echo '==> Compiling asciidoc files to generate Deckjs'
25 $(DOCTOR) -T /Users/bruel/dev/asciidoctor-backends/haml/deckjs/ -a slides \
26 -a data-uri -a deckjs_theme=$(DECK) \
27 -a icons -a iconsdir=$(ICONSDIR) \
28 -a images=$(IMAGESDIR) -a prof -o $@ $<
29 
30 %-sujet.html: %.$(EXT) $(DEP)
31 @echo '==> Compiling asciidoc files with Asciidoctor to generate HTML'
32 $(DOCTOR) -a compact -a theme=compact -b html5 -a numbered -a eleve \
33 -a data-uri $< -o $@
34 
35 %-prof.html: %.$(EXT) $(DEP)
36 @echo '==> Compiling asciidoc files with Asciidoctor to generate HTML'
37 $(DOCTOR) -a prof -a correction -a theme=compact -b html5 -a numbered \
38 -a data-uri $< -o $@

56. make (suite)

%.html: %.$(EXT) $(DEP)
@echo '==> Compiling asciidoc files with Asciidoctor to generate HTML'
$(DOCTOR) -a toc2 -b html5 -a numbered -a eleve $<

Exemple d’utilisation :

$ make wip.html
==> Compiling asciidoc files with Asciidoctor to generate HTML
asciidoctor -a icons -a iconsdir=images/icons -a images=images -a source-highlighter=pygments -a toc2 -b html5 -a numbered -a eleve wip.asc
...
$ make wip.html
make: 'wip.html' is up to date.

57. ant

build.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project default="main" name="EssaiBuild">
  <target name="main">
    <echo message="Version d'Ant utilisée: ${ant.version}"/>
    <javadoc doclet="de.mallox.doclet.PlantUMLDoclet"
    docletpath="plantUmlDoclet.jar"
    access="private"
    additionalparam=
    "-encoding utf-8 -J-DdestinationFile=uml.txt -J-DcreatePackages=false -J-DshowPublicMethods=true -J-DshowPublicConstructors=false -J-DshowPublicFields=true"
    >
      <packageset dir="../src">
        <include name="**"/>
      </packageset>
    </javadoc>

    <java jar="plantuml.jar" fork="true" maxmemory="128m">
      <arg value="uml.txt"/>
    </java>
  </target>
</project>

58. ant (suite)

Exemple d’utilisation :

$ ant main
Buildfile: build.xml
main:
[javadoc] Generating Javadoc
[javadoc] Javadoc execution
[javadoc] Loading source files for package pizzeriafactorysample...
[javadoc] Constructing Javadoc information...
[javadoc] PlantUMLDoclet.createPlantUml() -  start
[javadoc] open outputfile: uml.txt
[javadoc] write interfaces/ abstract classes...
[javadoc] write content...
...
[javadoc] skip association for Pizza --> java.lang.String
[javadoc] skip association for Pizza --> java.lang.String
[javadoc] skip association for Pizza --> java.lang.String
[javadoc] skip association for Pizza --> java.util.ArrayList
[javadoc] PlantUMLDoclet.createPlantUml() -  end
BUILD SUCCESSFUL
Total time: 9 seconds

59. ant (suite)

Exemple d’tilisation dans eclipse pour la génération de fichier du type TD.uml :

  1. Créer un répertoire tools et mettre dedans :

    • Plantuml.jar

    • Plantumldoclet.jar

    • build.xml

  2. Faire un Click_Droit sur build.xml et chosir Run_As  Ant Build …​

    Bien choisir celui avec les points de suspensions.

    build
  3. Dans l’onglet Environment :

    • Créer une nouvelle variable de nom Path et de valeur : le répertoire de la JDK (où se trouve javadoc ou javadoc.exe)

  4. Exécuter Run

    Faire Refresh dans le navigateur pour voir les 2 fichiers générés (uml.txt et uml.png).
  5. Il suffit de faire un Click_Droit sur build.xml et chosir Run_As  Ant Build …​ (SANS les 3 points de suspensions) pour relancer la génération du diagramme.

60. Maven

Maven est un outil Java.

Convention over Configuration

Exemples de conventions :

  • le code source est supposé se trouver dans ${basedir}/src/main/java

  • les différentes ressources dans ${basedir}/src/main/resources

  • les tests dans ${basedir}/src/test

  • un projet est supposé produire un fichier JAR

  • Maven suppose que vous voulez compiler en bytecode dans ${basedir}/target/classes

  • et ensuite créer votre fichier JAR distribuable dans ${basedir}/target

61. Maven (suite)

pom.xml
<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.sonatype.mavenbook</groupId>
  <artifactId>my-project</artifactId>
  <version>1.0</version>
</project>

La commande :

$ mvn install
  • va traiter les ressources,

  • compiler le source,

  • exécuter les tests unitaires,

  • créer un JAR, et

  • installer ce JAR dans le dépôt local.

La commande :

$ mvn site

va créer un fichier index.html dans target/site contenant des liens vers la JavaDoc et quelques rapports sur votre code source.

62. Maven (suite)

Pour comparer, voici l’équivalent ant :

<project name="my-project" default="dist" basedir=".">
  <description>
    simple example build file
  </description>
  <!-- set global properties for this build -->
  <property name="src" location="src/main/java"/>
  <property name="build" location="target/classes"/>
  <property name="dist"  location="target"/>

  <target name="init">
    <!-- Create the time stamp -->
    <tstamp/>
    <!-- Create the build directory structure used by compile -->
    <mkdir dir="${build}"/>
  </target>

  <target name="compile" depends="init"
    description="compile the source " >
    <!-- Compile the java code from ${src} into ${build} -->
    <javac srcdir="${src}" destdir="${build}"/>
  </target>

  <target name="dist" depends="compile"
    description="generate the distribution" >
    <!-- Create the distribution directory -->
    <mkdir dir="${dist}/lib"/>

    <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
    <jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
  </target>

  <target name="clean"
    description="clean up" >
    <!-- Delete the ${build} and ${dist} directory trees -->
    <delete dir="${build}"/>
    <delete dir="${dist}"/>
  </target>
</project>

63. Ivy

ivy.xml
<ivy-module version="2.0">
  <info organisation="org.apache" module="hello-ivy"/>
  <dependencies>
    <dependency org="commons-lang" name="commons-lang" rev="2.0"/>
    <dependency org="commons-cli" name="commons-cli" rev="1.0"/>
  </dependencies>
</ivy-module>
build.xml
<project xmlns:ivy="antlib:org.apache.ivy.ant" name="hello-ivy" default="run">

  ...

  <!-- =================================
  target: resolve
  ================================= -->
  <target name="resolve" description="--> retrieve dependencies with ivy">
    <ivy:retrieve />
  </target>
</project>

64. Graddle

Gradle combine la flexibilité de ant avec les conventions de Maven mais évite les inconvénients de XML.

build.gradle
task hello {
  doLast {
    println 'Hello world!'
  }
}
$ gradle hello
:hello
Hello world!

BUILD SUCCESSFUL

Total time: 3.486 secs

65. Design patterns

66. Introduction : importance des patrons

phdComics

Science is what we understand well enough to explain to a computer. Art is everything else we do.

— Donald Knuth

67. Strategy

68. Principes de conception

Principe de conception

Identifiez les aspects de votre code qui varient et séparez-les de ceux qui demeurent constant.

69. Principes de conception (suite)

Principe de conception

Programmer une interface, non une implémentation.

70. Principes de conception (suite)

Principe de conception

Préférez la composition à l’héritage.

71. Définition du patron

Design pattern : Stratégie (Strategy)

Stratégie définit une famille d’algorithmes, encapsule chacun d’eux et les rend interchangeables. Il permet à l’algorithme de varier indépendamment des clients qui l’utilisent.

strategy

72. Premier exemple d’utilisation

superCanardFinal a
superCanardFinal b
superCanardFinal c
Question
Pourquoi n’a-t’on pas utilisé Strategy pour afficher() ou nager()?

73. Autre exemple concret

L’exemple qui suit est tiré de ce cours.

Le problème

Vous avez une classe FileWriter qui a pour rôle d’écrire dans un fichier ainsi qu’une classe DBWriter. Dans un premier temps, ces classes ne contiennent qu’une méthode write() qui n’écrira que le texte passé en paramètre.

Au fil du temps, vous vous rendez compte que c’est dommage qu’elles ne fassent que ça et vous aimeriez bien qu’elles puissent écrire en différents formats (HTML, XML, etc.) : les classes doivent donc formater puis écrire.

74. Autre exemple concret (suite)

La solution

strategyWriter

75. Autre exemple concret (suite)

L’interface en PHP (code source ici)
<?php
interface Formater
{
  public function format($text);
}
?>

76. Autre exemple concret (suite)

La classe abstraite Writer (code source ici)
<?php
abstract class Writer
{
  // Attribut contenant l'instance du formateur que l'on veut utiliser.
  protected $formater;

  abstract public function write($text);

  // Nous voulons une instance d'une classe implémentant Formater en paramètre.
  public function __construct(Formater $formater)
  {
    $this->formater = $formater;
  }
}
?>

77. Autre exemple concret (suite)

La classe FileWriter (code source ici)
<?php
class FileWriter extends Writer
{
  // Attribut stockant le chemin du fichier.
  protected $file;

  public function __construct(Formater $formater, $file)
  {
    parent::__construct($formater);
    $this->file = $file;
  }

  public function write($text)
  {
    $f = fopen($this->file, 'w');
    fwrite($f, $this->formater->format($text));
    fclose($f);
  }
}
?>

78. Autre exemple concret (suite)

La classe DBWriter (code source ici)
<?php
class DBWriter extends Writer
{
  protected $db;

  public function __construct(Formater $formater, PDO $db)
  {
    parent::__construct($formater);
    $this->db = $db;
  }

  public function write ($text)
  {
    $q = $this->db->prepare('INSERT INTO lorem_ipsum SET text = :text');
    $q->bindValue(':text', $this->formater->format($text));
    $q->execute();
  }
}
?>

79. Autre exemple concret (suite)

Enfin, nous avons nos trois formateurs. L’un ne fait rien de particulier (TextFormater), et les deux autres formatent le texte en deux langages différents (HTMLFormater et XMLFormater).

80. Autre exemple concret (suite)

La classe TextFormater (code source ici)
<?php
class TextFormater implements Formater
{
  public function format($text)
  {
    return 'Date : ' . time() . "\n" . 'Texte : ' . $text;
  }
}
?>

81. Autre exemple concret (suite)

La classe HTMLFormater (code source ici)
<?php
class HTMLFormater implements Formater
{
  public function format($text)
  {
    return '<p>Date : ' . time() . '<br />' ."\n". 'Texte : ' . $text . '</p>';
  }
}
?>

82. Autre exemple concret (suite)

La classe XMLFormater (code source ici)
<?php
class XMLFormater implements Formater
{
  public function format($text)
  {
    return '<?xml version="1.0" encoding="ISO-8859-1"?>' ."\n".
           '<message>' ."\n".
           "\t". '<date>' . time() . '</date>' ."\n".
           "\t". '<texte>' . $text . '</texte>' ."\n".
           '</message>';
  }
}
?>

83. D’autres exemples

  • La fonction standard sort() de python

    >>> sorted("This is a test string from Andrew".split(), key=str.lower)
    ['a', 'Andrew', 'from', 'is', 'string', 'test', 'This']
  • Stratégie de cryptage en fonction de la taille d’un fichier

    File file = getFile();
     Cipher c = CipherFactory.getCipher( file.size() );
     c.performAction();
    
    // implementations:
    interface  Cipher  {
         public void performAction();
    }
    class InMemoryCipherStrategy implements Cipher {
             public void performAction() {
                 // load in byte[] ....
             }
    }
    class SwaptToDiskCipher implements Cipher {
             public void performAction() {
                 // swapt partial results to file.
             }
    }
Plus de détails ici

84. (non) Réutilisation

Les patrons ne sont pas réutilisables!

Il faut implémeter la solution qu’il représente à chaque fois.

85. Réutilisation (exception)

Exception : certains font l’objet d’une librairie (comme Observer de Java).

Par exemple le patron Singleton existe dans la bibliothèque standard du langage en Ruby. C’est un mixin qu’il suffit d’inclure dans la classe qui doit être un singleton.

class Klass
   include Singleton
   # ...
end

a,b  = Klass.instance, Klass.instance

a == b
# => true

Klass.new
# => NoMethodError - new is private ...

86. Association ou composition

On trouve deux modèles UML™ :

strategy compo
strategy assoc

87. Association ou composition (suite)

Et donc deux implémentations :

Composition ⇒ le composé encapsule les composants
public class Colvert extends Canard {

	protected Colvert() {
		this(new VolerAvecDesAiles(), new Cancan());
	}
...
c1 = new Colvert();
Association ⇒ le composant existe "en dehors"
...
vol = new VolerAvecDesAiles();
cri = new Cancan();
c1 = new Colvert(vol,cri);
...

88. Un peu d’histoire

1977

Alexander : patterns pour les architectures (les vraies)

Alexander
1987

Beck et Cunningham : patterns pour des interfaces utilisateurs

1988

Meyer : livre sur l’orienté objet (langage Eiffel), devenu la bible pour beaucoup de programmeurs (cf. [Meyer88])

1990-1995

Gamma, Helm, Johnson et Vlissides : LE livre de référence (cf. [GoF])

GoF
Les auteurs de ce livre sont connus comme les Gof pour « Gang of Four ».
2003

Martin : principes SOLID (cf. [Martin03])

2004

Craig Larman décrit des modèles de conception : les Patterns GRASP (cf. [Larman05])

89. Exemples de bons principes

SOLID:

  • Single Responsibility Principle

  • Open-Closed Principle

  • Liskov Substitution Principle

  • Interface Segregation Principle

  • Dependency Inversion Principle

90. Single Responsibility Principle

solid s
Responsabilité => Sujet à changement

91. Open-Closed Principle

solid o
Ouvert à l'extension mais fermé à la modification

92. Open-Closed Principle (suite)

Ainsi, une fois écrite et testée, une classe ne devrait être modifiée que pour être corrigée! Toute modification devrait être possible par extension.

93. Liskov Substitution Principle

liskov

94. Barbara Liskov est toujours active!

liskov2018

95. LSP : le principe

solid l
Une classe doit pouvoir être remplacée par une instance d'un de ses
sous-types, sans modifier la cohérence du programme

96. LSP : un exemple

Un carré est un rectangle particulier.

carre
Question
Peut-on toujours substituer un Carré à la place d’un Rectangle ?

97. Vraiment?

Réponse (Rectangle.java)
class Rectangle
{
	protected int m_width;
	protected int m_height;

	public void setWidth(int width){
		m_width = width;
	}

	public void setHeight(int height){
		m_height = height;
	}


	public int getWidth(){
		return m_width;
	}

	public int getHeight(){
		return m_height;
	}

	public int getArea(){
		return m_width * m_height;
	}
}

98. Vraiment?

Réponse (Square.java)
// Violation of Likov's Substitution Principle
class Square extends Rectangle
{
	public void setWidth(int width){
		m_width = width;
		m_height = width;
	}

	public void setHeight(int height){
		m_width = height;
		m_height = height;
	}

}

99. Vraiment?

Réponse (Square.java - suite)
class LspTest
{
	private static Rectangle getNewRectangle()
	{
		// it can be an object returned by some factory ...
		return new Square();
	}

	public static void main (String args[])
	{
		Rectangle r = LspTest.getNewRectangle();

		r.setWidth(5);
		r.setHeight(10);
		// User knows that r is a rectangle.
		// It assumes that he's able to set the width and height as for the base class

		System.out.println(r.getArea());
		// Now she's surprised to see that the area is 100 instead of 50.
	}
}

100. Et l’inverse?

rectangle

101. Même problème

Réponse (Rectangle.java)
class LspTest
{
	private static Square getNewSquare()
	{
		// it can be an object returned by some factory ...
		return new Rectangle();
	}

	public static void main (String args[])
	{
		Square s = LspTest.getNewSquare();

		s.setWidth(5);
		// User knows that r is a rectangle.
		// It assumes that he's able to set the width and height as for the base class

		System.out.println(s.getArea());
		// Now she's surprised to see that the area is 0 instead of 25.
	}
}

102. Interface Segregation Principle

solid i
Préférer plusieurs interfaces spécifiques pour chaque client plutôt qu'une seule interface générale

103. Dependency Inversion Principle

solid d
Il faut dépendre des abstractions, pas des implémentations

104. DIP : explications

Ce principe indique :

  • Les modules de haut niveau (abstraits) ne doivent pas dépendre des modules de bas niveau. Les deux doivent dépendre d’abstractions.

  • Les abstractions ne doivent pas dépendre des détails d’implémentation. C’est l’inverse : les détails doivent dépendre des abstractions.

Ainsi ce principe va à l’encontre de l’intuition classique.

105. Exemple (bad)

dip bad
Figure 1. Exemple de code violant le principe d’inversion des dépendances

106. Exemple (good)

dip
Figure 2. Exemple de code ne violant plus le principe d’inversion des dépendances

107. SOLID et patrons

QUESTION

Lesquels des 5 principes SOLID s’appliquent bien à Strategy ?

solid s

solid o

solid l

solid i

solid d

108. SOLID et patrons (éléments de réponses)

109. GRASP

The critical design tool for software development is a mind well educated in design principles. It is not the UML or any other technology.

— Craig Larman
2005

Il s’agit d’un ensemble de patrons, plutôt orientés conception (UML). Nous en aborderons certains au travers des exemples de ce module (cf. [Larman05]).

Notez que les principes SOLID ne s’appliquent pas qu’à la programmation objet. Pour une discussion sur leur application avec React (language fonctionnel), cf. https://dev.to/shadid12/can-you-apply-solid-principles-to-your-react-applications-46il.

110. Les patrons : comment ça marche ?

111. Intérêt

  • Réponses éprouvées à des problèmes récurrents

  • Vocabulaire commun

T’as qu’à utiliser une factory!

112. Patrons abordés

113. Patrons non abordés

  • Décorateur

  • Commande

  • Façade

  • Patron de méthode

  • Chaînes de responsabilité

  • Visiteur

  • Prototype

  • Mémento

  • Médiateur

  • Interprète

  • Poids-mouche

  • Monteur

  • Pont

114. Le patron Fabrique

115. Principes de conception

Design pattern : Fabrique (Factory)

Fabrique (simple) définit une interface pour la création d’un objet, mais en laissant à des sous-classes le choix des classes à instancier (voir aussi Fabrique abstraite).

fabrique

116. Premier exemple d’utilisation de patron

pizzeria0

117. Utilisation de patron (suite)

pizzeria b1

118. Autre exemple concret

Factory en PHP (source ici)
<?php
class DBFactory
{
  public static function load($sgbdr)
  {
    $classe = 'SGBDR_' . $sgbdr;

    if (file_exists($chemin = $classe . '.class.php'))
    {
      require $chemin;
      return new $classe;
    }
    else
    {
      throw new RuntimeException('La classe <strong>' . $classe . '</strong> n\'a pu être trouvée !');
    }
  }
}
?>

119. Autre exemple concret (suite)

Factory en PHP (source ici)
<?php
try
{
  $mysql = DBFactory::load('MySQL');
}
catch (RuntimeException $e)
{
  echo $e->getMessage();
}
?>

120. Autre exemple concret (AA)

Factory en java (source : votre module AA!)
public class XmlExprParser {

    public static Expression fromFile(String file) throws ... {
        SAXParserFactory spf = SAXParserFactory.newInstance();
        spf.setValidating(true);
        SAXParser sp = spf.newSAXParser();
        ExprHandler ep = new ExprHandler();
        sp.parse(file, ep);
        return ep.getResult();
    }

}

121. Un autre exemple concret

Factory en Java (source ici)
MazeGame ordinaryGame = new OrdinaryMazeGame();
MazeGame magicGame = new MagicMazeGame();

122. Un autre exemple concret (suite)

public abstract class MazeGame {
    private final List<Room> rooms = new ArrayList<>();

    public MazeGame() {
        Room room1 = makeRoom();
        Room room2 = makeRoom();
        room1.connect(room2);
        rooms.add(room1);
        rooms.add(room2);
    }

    abstract protected Room makeRoom();
}

123. Un autre exemple concret (suite)

public abstract class Room {
   abstract void connect(Room room);
}

public class MagicRoom extends Room {
   public void connect(Room room) {}
}

public class OrdinaryRoom extends Room {
   public void connect(Room room) {}
}

124. Un autre exemple concret (suite)

public class MagicMazeGame extends MazeGame {
    @Override
    protected Room makeRoom() {
        return new MagicRoom();
    }
}

public class OrdinaryMazeGame extends MazeGame {
    @Override
    protected Room makeRoom() {
        return new OrdinaryRoom();
    }
}

125. Mais c’est pas fini!

Reprenons nos pizzas vues en TD

pizzeria dep b1

126. Inversion des dépendances

pizzeria b1

127. Inversion des dépendances

  • Aucune variable ne doit contenir une référence à une classe concrète.

  • Aucune classe ne doit dériver d’une classe concrète.

  • Aucune classe ne doit redéfinir une méthode implémentée dans une classe de base.

128. Patron Fabrique

Pizzeria sec

129. Patron Fabrique

Pizzeria sec1
Figure 3. Problème de la dépendance des ingrédients

130. Patron Fabrique pour une famille de produits

ingredients

131. Fabrique d’ingrédients

public interface FabriqueIngredientsPizza {
  public Pate creerPate ();
  public Sauce creerSauce();
  public Fromage creerFromage();
  public Legumes[] creerLegumes();
  public Poivrons creerPoivrons();
  public Moules creerMoules();
}

132. Fabrique d’ingrédients (suite)

public class FabriqueIngredientsPizzaBrest implements FabriqueIngredientsPizza {
  public Pate creerPate() {
    return new PateFine();
  }
  public Sauce creerSauce() {
    return new SauceMarinara();
  }
  ...
}
public class FabriqueIngredientsPizzaStrasbourg implements FabriqueIngredientsPizza {
  public Pate creerPate() {
    return new PateEpaisse();
  }
  public Sauce creerSauce() {
    return new SauceTomateCerise();
  }
  ...
}

133. Fabrique d’ingrédients (utilisation)

public class PizzaFromage extends Pizza {
  FabriqueIngredientsPizza fabriqueIngredients;

  public PizzaFromage(FabriqueIngredientsPizza fabriqueIngredients) {
    this.fabriqueIngredients = fabriqueIngredients;
  }
  void preparer() {
    System.out.println("Préparation de " + nom);
    pate = fabriqueIngredients.creerPate();
    sauce = fabriqueIngredients.creerSauce();
    fromage = fabriqueIngredients.creerFromage();
  }
}

134. Fabrique abstraite

Nous sommes arrivé à une version du patron Fabrique appelée Fabrique Abstraite :

Fabrique (abstraite) fournit une interface pour la création de familles d’objets apparentés ou interdépendants, sans qu’il soit nécessaire de spécifier leurs classes concrètes (voir aussi Fabrique).

fabrique abstraite

135. Un nouveau diagramme UML très utile

etat1

Les diagrammes d'états-transitions (plus simplement diagramme d’état) d’UML™ décrivent le comportement interne d’un objet à l’aide d’un automate à états finis.

Les notions importantes de ce diagramme :

  • états

  • actions

  • événements déclencheurs

    • signaux

    • invocations de méthode

136. Transitions

etat2
Événement

Un signal, une invocation de méthode, etc.

Condition

Un booléen

Action

Affectation, invocation de méthode

137. Exemple de transitions

etat3

138. Refactoring

On peut remplacer les actions systématiques des transitions entrantes :

etat4

par une transition interne : entry :

etat5

139. Transitions internes

mdp
entry

permet de spécifier une activité qui s’accomplit quand on entre dans l’état.

exit

permet de spécifier une activité qui s’accomplit quand on sort de l’état.

do

commence dès que l’activité entry est terminée. Lorsque cette activité est terminée, une transition d’achèvement peut être déclenchée. Si une transition se déclenche pendant que l’activité do est en cours, cette dernière est interrompue et l’activité exit de l’état s’exécute.

140. Conditions

conditions

141. Exercices

QUESTION
  1. Réalisez un diagramme d’état UML représentant les différents états de l’eau (liquide, solide, gazeux).

  2. Réalisez un diagramme d’état UML représentant les états d’un étudiant de son arrivée en 1ère année à sa sortie de l’IUT en fonction des résultats aux différents examens (uniquement les années, pas les semestres).

142. Solutions

eau

143. Solutions

etud

144. Etats complexes

Un état peut lui-même être doté d’un comportement et donc représenter à lui seul une machine à état. Par exemple :

sousetats

145. Exercices

QUESTION

Intégrez les semestres aux diagramme précédent (étudiants)

146. Notion de concurrence

On peut représenter l’évolution de différentes machines de manière concurrente (parallèle). Par exemple :

concurrence

147. Exercices

QUESTION

Réalisez le diagramme d’état d’une machine à boisson rendant la monnaie.

148. Solution

149. Etat

Soit la machine à état suivante :

bonbons

150. Implémentation intuitive

Implémentation sans switch case
public void insererPiece() {
  if (etat == A_PIECE) {
    System.out.println("Vous ne pouvez plus insérer de pièces");
  } else if (etat == EPUISE) {
    System.out.println("Vous ne pouvez pas insérer de pièce, nous sommes en rupture de stock");
  } else if (etat == VENDU) {
    System.out.println("Veuillez patienter, le bonbon va tomber");
  } else if (etat == SANS_PIECE) {
    etat = A_PIECE;
    System.out.println("Vous avez inséré une pièce");
  }
}

151. Erreur d’implémentations

  • Ce code n’adhère pas au principe Ouvert-Fermé.

  • Cette conception n’est pas orientée objet.

  • Les transitions ne sont pas explicites. Elles sont enfouies au milieu d’un tas d’instructions conditionnelles.

  • Nous n’avons pas encapsulé ce qui varie.

  • Les ajouts ultérieurs sont susceptibles de provoquer des bugs dans le code.

152. Une meilleure implémentation

  1. Définir une nouvelle interface Etat qui contiendra une méthode pour chaque action

  2. Implémenter une classe pour chaque Etat. Elles seront responsable du comportement.

  3. Se débarrasser de toutes les instructions conditionnelles et les remplacer par une délégation à la classe adéquate.

153. Illutration

Etape 1 : les états comme implémentations d’une interface

bonbons

bonbons dc

154. Illustration (suite)

Etape 2 : implémentation des méthodes de l’interface

public class EtatSansPiece implements Etat {

  // Va falloir remplir ici...

  public void insererPiece() {
    System.out.println("Vous avez inséré une pièce");
    // changer d'état si besoin

  }
  ...
}

155. Illustration (suite)

Etape 3 : utilisation

public class Distributeur {

  Etat etat = new EtatSansPiece(); // état initial
  ...
  public void insererPiece() {
    etat.insererPiece(); // on délègue à l'état le soin de réagir
  }
...
}

156. Illustration (suite)

Etape 4 (enfin, retour sur l’étape 2) : une solution possible…​

public class EtatSansPiece implements Etat {
  Distributeur distributeur; // référence au distributeur qu'on gère

  public EtatSansPiece(Distributeur distributeur) {
    this.distributeur = distributeur;
  }

  public void insererPiece() {
    System.out.println("Vous avez inséré une pièce");
    distributeur.setEtat(distributeur.getEtatAPiece());
  }
  ...
}

157. Le patron Etat

Etat permet à un objet de modifier son comportement, quand son état interne change. Tout se passe comme si l’objet changeait de classe.

etat
QUESTION

Que pensez-vous de notre solution précédente par rapport à ce diagramme UML?

158. Solution

L’état possède une référence vers le contexte (Distributeur dans notre exemple).

State
Figure 4. Une autre implémentation (source : https://sourcemaking.com/design_patterns/state)

159. Point sur le projet MPA

160. De belles interfaces

projetUIWeb
projetUIFX
Figure 6. JavaFX

161. Un peu de tout sur la docs

  • Pas mal du tout sur la doc utilisateur

  • Pas beaucoup plus que la javadoc sur la doc technique

162. Du n’importe quoi sur certaines docs

docGarde
Figure 7. Une documentation utilisateur qui commence bien…​
 …​ pour finir par 2 pages + 1 sommaire !!"

163. Bien sur les fonctionnalités

  • beaucoup de code complets

  • mais ce n’était pas le plus importantes

164. Faible sur les tests

  • pas confiance dans vos applications pour les utiliser tel que

165. Observateur

observer

166. Motivation

observer illustration

167. Définition

Observateur définit une relation entre objets de type un-à-plusieurs, de façon que, lorsqu’un objet change d’état, tous ceux qui en dépendent en soient notifiés et soient mis à jour automatiquement.

observateur

168. Application

Le patron Observer est utilisable dans de nombreuses situations :

  • Quand un concept a deux aspects, l’un dépendant de l’autre. Encapsuler ces aspects dans des objets séparés permet de les utiliser et les laisser évoluer de manière indépendante.

  • Dès que le changement d’un objet entraîne le changement de plusieurs autres.

  • Dès qu’un objet doit en notifier un certain nombre d’autres sans les connaitre.

169. Observer en Java

Java fournit des classes Observable/Observer pour le patron Observer. La classe java.util.Observable est la classe de base pour les sujets. Ainsi, toute classe qui veut être observée étant cette classe dont voici les caractéristiques :

  • fournit des méthodes pour ajouter/enlever des observateurs

  • fournit des méthodes pour notifier les observateurs

  • une sous-classe concrète doit seulement s’occuper de notifier à chque méthode modifiant l’état des objets (mutators)

  • utilise un vecteur stoquant les références des observateurs

L’interface java.util.Observer correspond aux observateurs qui doivent implémenter cette interface.

170. La classe java.util.Observable

Voici la liste des méthodes de java.util.Observable :

1 public Observable()
2 public synchronized void addObserver(Observer o)
3 protected synchronized void setChanged()
4 public synchronized void deleteObserver(Observer o)
5 protected synchronized void clearChanged()
6 public synchronized boolean hasChanged()
7 public void notifyObservers(Object arg)
8 public void notifyObservers()

171. L’interface java.util.Observer

java.util.Observer
 1 /**
 2 * This method is called whenever the observed object is changed. An
 3 * application calls an observable object's notifyObservers method to have all
 4 * the object's observers notified of the change.
 5 *
 6 * Parameters:
 7 * o - the observable object
 8 * arg - an argument passed to the notifyObservers method
 9 */
10 public abstract void update(Observable o, Object arg)

172. Une implémentation du MVC : les JTable java

JTable

173. Le principe

TD6 MVC 1

174. L’architecture

JTable

175. Adaptateur

adaptateur

176. Le problème

On veut pouvoir :

  • utiliser une classe existante, mais dont l’interface ne coïncide pas avec celle escomptée.

  • créer une classe réutilisable qui collabore avec des classes sans relations avec elle et encore inconnues, c’est-à-dire avec des classes qui n’auront pas nécessairement des interfaces compatibles.

  • vous avez besoin d’utiliser plusieurs sous-classes existantes, mais l'adaptation de leur interface par dérivation de chacune d’entre elles est impraticable. Un adaptateur objet peut adapter l’interface de sa classe parente.

    Ce dernier cas ne concerne que le cas "adaptateur d’objet"

177. Exemple concret : le retour des canards

  • L’existant :

Le Canard
public interface Canard {
  public void cancaner();
  public void voler();
}

public class Colvert implements Canard {
  public void cancaner() {
    System.out.println("Coincoin");
  }
  public void voler() {
    System.out.println("Je vole");
  }
}

178. Exemple concret : le retour des canards

  • Le "presque canard" :

Le Presque Canard
public interface Dindon {
  public void glouglouter();
  public void voler();
}

public class DindonSauvage implements Dindon {
  public void glouglouter() {
    System.out.println("Glouglou");
  }
  public void voler() {
    System.out.println("Je ne vole pas loin");
  }
}

179. Exemple concret : le problème

Vous êtes à court d’objets Canard et vous aimeriez utiliser des objets Dindon à la place!

TurkeyToDuck

[detail]#Un "presqueCanard" (Crédit Dessin C. Aribaud, 2A 2016-2017)

180. Exemple concret : la solution

Mise en oeuvre du patron Adaptateur
public class AdaptateurDindon implements Canard {
  Dindon dindon;

  ...

  public void cancaner() {
    dindon.glouglouter();
  }

  public void voler() {
    // Adaptation du vol
    for(int i=0; i < 5; i++) {
      dindon.voler();
    }
  }
}

181. Le patron Adaptateur

Adaptateur (Adaptor) permet de convertir l’interface d’une classe en une autre conformément à l’attente du client. L’Adaptateur permet à des classes de collaborer, alors qu’elles n’auraient pas pu le faire du fait d’interfaces incompatibles.

adapteur

182. Le patron Adaptateur

adaptor google

183. Le patron Visiteur

les visiteurs

184. Le problème

Quelques situations à problème :

  • Une structure d’objets contient beaucoup de classes différentes d’interfaces distinctes, et vous désirez réaliser des opérations sur ces objets qui dépendent de leurs classes concrètes.

  • Il s’agit d’effectuer plusieurs opérations distinctes et sans relation entre elles, sur les objets d’une structure, et ceci en évitant de polluer leurs classes avec ces opérations.

  • Les classes qui définissent la structure objet changent rarement, mais on doit souvent définir de nouvelles opérations sur cette structure.

185. Illustration

186. Step 1

Définir une interface pour représenter les éléments de la structure.

visitor2

187. Step 1 (suite)

visitor3
public interface ComputerPart {
   ...
}

188. Step 2

Anticiper l’utilisation du visiteur.

public interface ComputerPartVisitor {
	...
}
public interface ComputerPart {
   public void accept(ComputerPartVisitor computerPartVisitor);
}

189. Step 3

Créer les classes concrètes qui implémentent l’interface.

public class Keyboard implements ComputerPart {

   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}

…​

190. Step 3 (suite)

public class Computer implements ComputerPart {

   ComputerPart[] parts;

   public Computer(){
      parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};
   }


   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      for (int i = 0; i < parts.length; i++) {
         parts[i].accept(computerPartVisitor);
      }
      computerPartVisitor.visit(this);
   }
}

191. Step 4

Définir l’interface pour représenter le visiteur.

public interface ComputerPartVisitor {
	public void visit(Computer computer);
	public void visit(Mouse mouse);
	public void visit(Keyboard keyboard);
	public void visit(Monitor monitor);
}

192. Step 5

Créer des visiteurs concrets.

visitor4

193. Step 5 (suite)

public class DisplayVisitor implements ComputerPartVisitor {

   @Override
   public void visit(Computer computer) {
      System.out.println("Displaying Computer.");
   }

   @Override
   public void visit(Mouse mouse) {
      System.out.println("Displaying Mouse.");
   }

   @Override
   public void visit(Keyboard keyboard) {
      System.out.println("Displaying Keyboard.");
   }

   @Override
   public void visit(Monitor monitor) {
      System.out.println("Displaying Monitor.");
   }
}

194. Step 6

Utiliser le visiteur DisplayVisitor.

public class VisitorPatternDemo {
   public static void main(String[] args) {

      ComputerPart computer = new Computer();
      computer.accept(new DisplayVisitor());
   }
}

195. Step 7 (final)

Verify the output.

Displaying Mouse.
Displaying Keyboard.
Displaying Monitor.
Displaying Computer.

196. Le patron Visiteur

Visiteur (Visitor) permet la représentation d’une opération applicable aux éléments d’une structure d’objet.

Il définit une nouvelle opération, sans qu’il soit nécessaire de modifier la classe des éléments sur lesquels elle agit.

197. Le patron Visiteur (structure)

visitor dc

198. Le patron Visiteur (comportement)

visitor seq

199. Le patron Visiteur (Google)

visitor google

200. Avantages/Inconvénients

Avantages :

  • Permet d’ajouter des opérations à la structure d’un Composite sans modifier la structure elle-même.

  • L'ajout de nouvelles opérations est relativement facile.

  • Le code des opérations exécutées par le Visiteur est centralisé.

Inconvénients :

  • L’encapsulation des classes du Composite est brisée.

  • Comme une fonction de navigation est impliquée, les modifications de la structure du Composite sont plus difficiles.

201. Exemples d’utilisation

  • calcul sur un ensemble structuré d’éléments

  • génération de rapports ou de code

  • …​

202. Exemple concret d’utilisation en Java

Exemple tiré de ce site.
public interface ItemElement {

  public int accept(ShoppingCartVisitor visitor);
}

203. Exemple concret (suite)

public class Book implements ItemElement {

  private int price;
  private String isbnNumber;

  public Book(int cost, String isbn){
    this.price=cost;
    this.isbnNumber=isbn;
  }

  public int getPrice() {
    return price;
  }

  public String getIsbnNumber() {
    return isbnNumber;
  }

  @Override
  public int accept(ShoppingCartVisitor visitor) {
    return visitor.visit(this);
  }

}

204. Exemple concret (suite)

public class Fruit implements ItemElement {

  private int pricePerKg;
  private int weight;
  private String name;

  public Fruit(int priceKg, int wt, String nm){
    this.pricePerKg=priceKg;
    this.weight=wt;
    this.name = nm;
  }

  public int getPricePerKg() {
    return pricePerKg;
  }

  public int getWeight() {
    return weight;
  }

  public String getName(){
    return this.name;
  }

  @Override
  public int accept(ShoppingCartVisitor visitor) {
    return visitor.visit(this);
  }
}

205. Exemple concret (suite)

public interface ShoppingCartVisitor {

  int visit(Book book);
  int visit(Fruit fruit);
}

206. Exemple concret (suite)

public class ShoppingCartVisitorImpl implements ShoppingCartVisitor {

  @Override
  public int visit(Book book) {
    int cost=0;
    //apply 5$ discount if book price is greater than 50
    if(book.getPrice() > 50){
      cost = book.getPrice()-5;
    } else cost = book.getPrice();
    System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);
    return cost;
  }

  @Override
  public int visit(Fruit fruit) {
    int cost = fruit.getPricePerKg()*fruit.getWeight();
    System.out.println(fruit.getName() + " cost = "+cost);
    return cost;
  }
}

207. Exemple concret (suite)

public class ShoppingCartClient {

  public static void main(String[] args) {
    ItemElement[] items = new ItemElement[]{new Book(20, "1234"),new Book(100, "5678"),
    new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple")};

    int total = calculatePrice(items);
    System.out.println("Total Cost = "+total);
  }

  private static int calculatePrice(ItemElement[] items) {
    ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
    int sum=0;
    for(ItemElement item : items){
      sum = sum + item.accept(visitor);
    }
    return sum;
  }

}

208. Exemple concret (suite)

Book ISBN::1234 cost =20
Book ISBN::5678 cost =95
Banana cost = 20
Apple cost = 25
Total Cost = 160

209. Proxy

210. Le problème

On a besoin de références à un objet, qui soient plus créatives et plus sophistiquées qu’un simple pointeur.

211. Le patron Proxy

Procuration (Proxy) fournit à un tiers un mandataire ou un remplaçant, pour contrôler l’accès à cet objet.

proxy

212. Le patron Proxy

proxy google

213. Utilisations

  • Une procuration à distance fournit un représentant local d’un objet situé dans un espace adresse différent.

  • Une procuration virtuelle crée des objets lourds à la demande.

  • Une procuration de protection contrôle l’accès à l’objet original. Les procurations de protection sont utiles quand les objets doivent satisfaire différents droits d’accès.

  • Une référence intelligente est le remplaçant d’un pointeur brut, qui réalise des opérations supplémentaires, lors de l’accès à l’objet. Quelques utilisations typiques sont :

    • décompte du nombre des références faites à un objet réel, de sorte que celui-ci puisse être libéré automatiquement, dés qu’il n’y a plus de références ;

    • charger en mémoire un objet persistant quand il est référencé pour la première fois ;

    • vérifier, avant d’y accéder, que l’objet réel est verrouillé, pour être sûr qu’aucun autre objet ne pourra le changer.

214. Exemple concret : RMI

Remote Method Invocation est une méthode d’accès à un service à distance.

Interface distante
import java.rmi.*;
public interface MonService extends Remote {
  public String direBonjour() throws RemoteException;
}

215. Exemple concret : RMI (suite)

Le service distant (l’implémentation)
import java.rmi.*;
import java.rmi.server.*;

public class MonServiceImpl extends UnicastRemoteObject implements MonService {
  public String direBonjour() {
    return "Le serveur dit 'Bonjour'";
  }
  public MonServiceImpl() throws RemoteException {}
  public static void main (String[] args) {
    try {
      MonService service = new MonServiceImpl();
      Naming.rebind("BonjourDistant", service);
    } catch(Exception ex) {
      ex.printStackTrace();
    }
  }
}

216. Exemple concret : RMI (suite)

Utilisation
MonService service =
  (MonService) Naming.lookup("rmi://127.0.0.1/BonjourDistant");
...
service.direBonjour();

217. Itérateur

218. Le problème

On veut pouvoir :

  • pour accéder au contenu d’un objet d’un agrégat sans en révéler la représentation interne ;

  • pour gérer simultanément plusieurs parcours dans des agrégats d’objets ;

  • pour offrir une interface uniforme pour les parcours au travers de diverses structures agrégats (c’est-à-dire, pour permettre l’itération polymorphe).

219. Le patron Itérateur

Itérateur (Iterator) fournit un moyen d’accès séquentiel aux éléments d’un agrégat d’objets, sans mettre à découvert la représentation interne de celui-ci.

iterateur

220. Le patron Itérateur

iterateur google

221. Exemple concret

Voici un exemple en Ruby :

# Saluer tout le monde
def say_hi
  if @names.nil?
    puts "..."
  elsif @names.respond_to?("each")
    # @names est une liste de noms : traitons-les uns par uns
    @names.each do |name|
      puts "Hello #{name}!"
    end
  else
    puts "Hello #{@names}!"
  end
end

222. Le patron Composite

223. Le problème

On veut pouvoir :

  • représenter des hiérarchies de l’individu.

  • que le client n’ait pas à se préoccuper de la différence entre "combinaisons d’objets" et "objets individuels". Les clients pourront traiter de façon uniforme tous les objets de la structure composite.

224. Le patron Composite

Composite permet de composer des objets en des structures arborescentes pour représenter des hiérarchies composant/composé. Permet au client de traiter d’une façon unique les objets et les combinaisons d’objets.

composite

225. Le patron Composite

composite google

226. Exemple concret

Exemple de Composite Java
import java.util.ArrayList;

interface Graphic {
    public void print();
}

class CompositeGraphic implements Graphic {

  private ArrayList<Graphic> mChildGraphics = new ArrayList<Graphic>();

  public void print() {
    for (Graphic graphic : mChildGraphics) {
      graphic.print();
    }
  }

  public void add(Graphic graphic) {
    mChildGraphics.add(graphic);
  }

  public void remove(Graphic graphic) {
    mChildGraphics.remove(graphic);
  }
}

227. un "Anti" exemple

Que pensez-vous de cette définition de Composite ?

anti composite
On appelle ces modèles des "Patrons abîmés" (anti-patterns).

228. Retour sur le refactoring Banque

229. Le problème

refactoring1

230. Le problème (suite)

Remplacer tous ces switch cases

continuer = true;
while (continuer) {
  AAB.afficherMenu(monAg);
  choix = lect.next();
  choix = choix.toLowerCase();
  switch (choix) {
    ...
    case "p" :
      System.out.print("Propriétaire -> ");
      nom = lect.next();
      AAB.comptesDUnPropretaire (monAg, nom);
    break;
    ...

231. Le problème (suite)

  • Afficher une liste séparemment du switch

    //AAB.afficherMenu(monAg);
      System.out.println("Menu de " + ag.getNomAgence() + " (" + ag.getLocAgence() + ")");
      System.out.println("l - Liste des comptes de l'agence");
      ...
      System.out.println("p - voir les comptes d'un Propriétaire (par son nom)");
      ...
      System.out.print("Choix -> ");
    }
  • Tester tous les choix pour actionner la bonne option

      ...
      case "p" :
        System.out.print("Propriétaire -> ");
        nom = lect.next();
        AAB.comptesDUnPropretaire (monAg, nom);
      break;
      ...

232. Une solution

  • Des listes

  • Des options de menu qui encapsulent l’action à réaliser

233. Une solution (suite)

public interface ActionList extends Action {

  public String listTitle();
  public int size();

  public boolean addAction(Action ac);
  public boolean removeAction(Action ac);

  public String[] listOfActions() ;

}

234. Une solution (suite)

Une interface pour les options de menu
public interface Action  {

  public String actionMessage ();
  public void execute(AgenceBancaire ag);
}

235. Une solution (suite)

Une classe concrète par option de menu
public class Action1 implements Action {

  private String lineMessage;

  ...
  public String actionMessage() {
    return this.lineMessage;
  }

  public void execute(AgenceBancaire ab) {

    ...
    ab.afficher();
  }
}
Utilisation de l’action
  action.execute(ab);

236. Une solution (suite)

Lien entre liste et action
Action a1 = new Action1("Liste des comptes de l'agence");
Action a2 = new Action2("Voir un compte (par son numéro)");
Action a3 = new Action3(...);

ActionList al1 = new ActionListAgenceBancaire("Menu Général");

al1.addAction(a1);
al1.addAction(a2);

237. Une solution (suite)

Lien entre liste et action : choix dans la liste
public void execute(AgenceBancaire ab) throws Exception {
  ...
  while (true) {
    this.printMenu();

    choice = this.readResponse();
    ...
    this.myMenu.get(choice).execute(ab);
    ...

238. Une solution (suite)

La liste peut elle-même être une option de menu (une action)!
public interface ActionList extends Action {
}

239. Pour aller plus loin avec les patrons…​

plusloin
Figure 8. Crédit photo : http://blogs.radiocanut.org/

240. Partagez votre vocabulaire

  • Dans les réunions de conception (pas nécessairement avec le client)

  • Avec les autres développeurs

  • Dans la documentation de votre architecture

  • Dans les commentaires du code et les conventions de nommage

  • Dans les groupes/blogs de développeurs

  • (pas pendant les exams!)

241. Ne foncez pas tête baissée

Quelques conseils :

  • Les patterns sont des outils, non des règles.

    ⇒ Rien n’empêche de les modifier et de les adapter à votre problème.

  • Ne visez l’extensibilité que si la question se pose réellement dans la pratique, pas si elle est uniquement hypothétique.

  • Ne vous emballez pas et recherchez la simplicité.

    ⇒ Si vous trouvez une solution plus simple que l’emploi d’un pattern, n’hésitez pas !

  • éliminez ce qui n’est pas vraiment nécessaire.

    ⇒ N’ayez pas peur de supprimer un design pattern inutile de votre conception.

242. Les autres types de patrons

Il n’y a pas que les 3 types de patrons que l’on a vu :

  • De création

  • Structurels

  • Comportementaux

  • des exemples?

    Warning
    QUESTION

    Un ou deux exemples de chaque?

243. Les autres types de patrons (suite)

Il y a par exemple :

  • Les patrons d’architecture

  • Les patrons d’application

  • Les patrons de domaine

  • Les patrons de processus

  • Les patrons d’organisation

  • Les patrons de conception d’interfaces utilisateur

244. Les anti-patrons

anti patrons

245. Les anti-patrons

  • Des solutions souvent appliquées à tort à des problèmes récurrents

  • Décrit comment partir d’un problème pour arriver à une mauvaise solution

  • Vous dit pourquoi une mauvaise solution est attrayante

  • Suggère d’autres patrons applicables pouvant fournir de meilleures solutions

246. Tous les patrons qu’on a pas vu

Il y en a beaucoup :

  • Chaîne de responsabilité

  • Commande

  • Décorateur

  • Façade

  • Interprète

  • Médiateur

  • Mémento

  • Monteur

  • Patron de méthode

  • Poids-mouche

  • Pont

  • Prototype

247. Injection de dépendances

Nous n’avons pas le temps de traiter ce point, mais cf. par exemple https://blog.angularindepth.com/why-do-we-have-dependency-injection-in-web-development-f8815e593b38.

Glossaire et définition

Ces définitions reprennent les définitions vues dans ce livre, en les organisant par grands types de patrons de conception : de création, comportementaux et structurels.

Patrons de création

Singleton

Singleton garantit qu’une classe n’a qu’une seule instance et fournit un point d’accès global à cette instance.

singleton

Patrons de création (suite)

Fabrique

Fabrique (simple) définit une interface pour la création d’un objet, mais en laissant à des sous-classes le choix des classes à instancier (voir aussi Fabrique abstraite).

fabrique

Patrons de création (suite)

Fabrique abstraite

Fabrique (abstraite) fournit une interface pour la création de familles d’objets apparentés ou interdépendants, sans qu’il soit nécessaire de spécifier leurs classes concrètes (voir aussi Fabrique).

fabrique abstraite

Patrons comportementaux

État (Stateuk)

Etat permet à un objet de modifier son comportement, quand son état interne change. Tout se passe comme si l’objet changeait de classe.

etat

Patrons comportementaux (suite)

Itérateur (Iteratoruk)

Itérateur (Iterator) fournit un moyen d’accès séquentiel aux éléments d’un agrégat d’objets, sans mettre à découvert la représentation interne de celui-ci.

iterateur

Patrons comportementaux (suite)

Observateur (Observeruk)

Observateur définit une relation entre objets de type un-à-plusieurs, de façon que, lorsqu’un objet change d’état, tous ceux qui en dépendent en soient notifiés et soient mis à jour automatiquement.

observateur

Patrons comportementaux (suite)

Stratégie (Strategyuk)

Stratégie définit une famille d’algorithmes, encapsule chacun d’eux et les rend interchangeables. Il permet à l’algorithme de varier indépendamment des clients qui l’utilisent.

strategy

Patrons comportementaux (suite)

Visiteur (Visitoruk)

Visiteur (Visitor) permet la représentation d’une opération applicable aux éléments d’une structure d’objet. Il définit une nouvelle opération, sans qu’il soit nécessaire de modifier la classe des éléments sur lesquels elle agit.

visitor dc
visitor seq

Patrons structurels

Adaptateur (Adaptoruk)

Adaptateur (Adaptor) permet de convertir l’interface d’une classe en une autre conformément à l’attente du client. L’Adaptateur permet à des classes de collaborer, alors qu’elles n’auraient pas pu le faire du fait d’interfaces incompatibles.

adapteur

Patrons structurels (suite)

Composite

Composite permet de composer des objets en des structures arborescentes pour représenter des hiérarchies composant/composé. Permet au client de traiter d’une façon unique les objets et les combinaisons d’objets.

composite

Patrons structurels (suite)

Procuration (Proxyuk)

Procuration (Proxy) fournit à un tiers un mandataire ou un remplaçant, pour contrôler l’accès à cet objet.

proxy

Références

  • [Cysboy] Apprenez à programmer en Java. Par cysboy. Disponible ici (le 2020-11-04).

  • [Freeman04] Design Pattren - Head First. Bert Bates, Eric Freeman, Elisabeth Freeman, Kathy Sierra. O’Reilly, 09/2004.

  • [Freeman05] Tête la première : Design Pattern. Bert Bates, Eric Freeman, Elisabeth Freeman, Kathy Sierra. Editions O’Reilly. 2005.

  • [GoF] Design Patterns: Elements of reusable object oriented software. 1994.

  • GOPROD - De bonnes pratiques au service de la conception orientée objets. Disponible ici (le 2020-11-04).

  • [Larman05] Larman, Craig. Applying UML and Patterns – An Introduction to Object-Oriented Analysis and Design and Iterative Development (3rd ed.). Prentice Hall. 2005. ISBN 0-13-148906-2.

  • [Martin03] “Principles Of OOD”, Robert C. Martin (“Uncle BOB”), http://butunclebob.com.

  • [Meyer88] Meyer, Bertrand. Object-Oriented Software Construction. Prentice Hall. 1988. ISBN 0-13-629049-3.

  • [SOLID] https://blogs.msdn.microsoft.com/cdndevs/2009/07/15/the-solid-principles-explained-with-motivational-posters/

About…​

Document réalisé par Jean-Michel Bruel via Asciidoctor (version 2.0.10) de 'Dan Allen', lui même basé sur AsciiDoc. Pour l’instant ce document est libre d’utilisation et géré par la 'Licence Creative Commons'. Licence Creative Commons licence Creative Commons Paternité - Partage à l'Identique 3.0 non transposé.